#include <cassert>
#include <iostream>
#include <map>
#include <deque>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include "server.h"

using namespace std;

const int N = 100 * 92323;

class DeterministicPseudoRandomServer: public Server {
	public:
		enum ColType { INT, DOUBLE, BOOL };

		DeterministicPseudoRandomServer(int n): _n(n) {}

		// We need to print everything here, because we don't know amount
		// of output columns before consuming everything, and we don't know
		// when we finally consumed everything.
		~DeterministicPseudoRandomServer() {
			int amount = 0;
			int columns = 0;

#ifndef NO_OUTPUT
			// check that amount of data in output columns is equal
			for (map<int, deque<int32_t> >::const_iterator i = _intQueue.begin(); i != _intQueue.end(); ++i) {
				if (amount == 0) {
					amount = i->second.size();
				} else {
					assert(amount == (int) i->second.size());
				}

				columns = max(columns, i->first + 1);
			}

			for (map<int, deque<double> >::const_iterator i = _doubleQueue.begin(); i != _doubleQueue.end(); ++i) {
				if (amount == 0) {
					amount = i->second.size();
				} else {
					assert(amount == (int) i->second.size());
				}

				columns = max(columns, i->first + 1);
			}

			for (map<int, deque<bool> >::const_iterator i = _boolQueue.begin(); i != _boolQueue.end(); ++i) {
				if (amount == 0) {
					amount = i->second.size();
				} else {
					assert(amount == (int) i->second.size());
				}

				columns = max(columns, i->first + 1);
			}

			// print everything in rows
			for (int i = 0; i < amount; ++i) {
				for (int col = 0; col < columns; ++col) {
					if (_intQueue.find(col) != _intQueue.end()) {
						cout << setw(15) << _intQueue[col].front();
						_intQueue[col].pop_front();
					} else if (_doubleQueue.find(col) != _doubleQueue.end()) {
						cout << setw(15) << _doubleQueue[col].front();
						_doubleQueue[col].pop_front();
					} else {
						assert(_boolQueue.find(col) != _boolQueue.end());
						cout << setw(7) << boolalpha << _boolQueue[col].front();
						_boolQueue[col].pop_front();
					}
				}

				cout << "\n";
			}
#endif
		}

		virtual int GetDoubles(int column_index, int number, double* destination) {
			initColumnIfNeeded(column_index, DOUBLE);

			int amount = min(number, _dataLeft[column_index]);

			for (int i = 0; i < amount; ++i) {
				destination[i] = normal(column_index);
			}

			_dataLeft[column_index] -= amount;
			return amount;
		}

		virtual int GetInts(int column_index, int number, int32_t* destination) {
			initColumnIfNeeded(column_index, INT);

			int amount = min(number, _dataLeft[column_index]);

			for (int i = 0; i < amount; ++i) {
				destination[i] = rand(column_index) % 103;
			}

			_dataLeft[column_index] -= amount;
			return amount;
		}

		virtual int GetByteBools(int column_index, int number, bool* destination) {
			initColumnIfNeeded(column_index, BOOL);

			int amount = min(number, _dataLeft[column_index]);

			for (int i = 0; i < amount; ++i) {
				destination[i] = (bool) (rand(column_index) % 2);
			}

			_dataLeft[column_index] -= amount;
			return amount;
		}

		virtual int GetBitBools(int column_index, int number, char* destination) {
			assert(false); // NOT IMPLEMENTED
			return 0;
		}

		virtual void ConsumeDoubles(int column_index, int number, const double* destination) {
			checkOutputType(column_index, DOUBLE);

#ifndef NO_OUTPUT
			for (int i = 0; i < number; ++i) {
				_doubleQueue[column_index].push_back(destination[i]);
			}
#endif
		}

		virtual void ConsumeInts(int column_index, int number, const int32_t* destination) {
			checkOutputType(column_index, INT);

#ifndef NO_OUTPUT
			for (int i = 0; i < number; ++i) {
				_intQueue[column_index].push_back(destination[i]);
			}
#endif
		}

		virtual void ConsumeByteBools(int column_index, int number, const bool* destination) {
			checkOutputType(column_index, BOOL);

#ifndef NO_OUTPUT
			for (int i = 0; i < number; ++i) {
				_boolQueue[column_index].push_back(destination[i]);
			}
#endif
		}

		virtual void ConsumeBitBools(int column_index, int number, const char* destination) {
			assert(false); // NOT IMPLEMENTED
		}

	private:
		void initColumnIfNeeded(int column_index, ColType type) {
			if (_dataLeft.find(column_index) == _dataLeft.end()) {
				_dataLeft[column_index] = _n;
				_types[column_index] = type;
				_randomState[column_index] = column_index;
			}
		}

		void checkOutputType(int column_index, ColType type) {
			if (_outputTypes.find(column_index) == _outputTypes.end()) {
				_outputTypes[column_index] = type;
			} else {
				assert(_outputTypes[column_index] == type);
			}
		}

		int32_t rand(int32_t column_index) {
			int32_t state = _randomState[column_index];
			state = state * 1526089 + 319993 + (~state >> 15);
			_randomState[column_index] = state;
			return state;
		}

		// Returns a U(0, 1) variable.
		double uniform(int column_index) {
		  return (double) (1 + (rand(column_index) % ((1 << 30) - 1))) / (double) (1 << 30);
		}

		// Returns a N(0, 1) variable using the Box-Muller method.
		double normal(int column_index) {
		  return sqrt(-2. * log(abs(uniform(column_index)))) * cos(8 * atan(1) * uniform(column_index));
		}

		int _n;
		map<int, int> _dataLeft;
		map<int, int32_t> _randomState;
		map<int, ColType> _types;
		map<int, ColType> _outputTypes;
		map<int, deque<int32_t> > _intQueue;
		map<int, deque<double> > _doubleQueue;
		map<int, deque<bool> > _boolQueue;
};

Server* CreateServer(int query_id) {
	ios_base::sync_with_stdio(false);
	return new DeterministicPseudoRandomServer(N);
}
